import axios, {
    AxiosError,
    AxiosPromise,
    AxiosRequestConfig,
    AxiosResponse,
    AxiosStatic,
} from "axios";

import store from "../redux";
import { LOGOUT } from "../redux/user/types";

export interface Response {
    code: number;
    data?: any;
    message: string;
}

type requestFn = (
    url: string,
    params?: Object,
    data?: Object | null
) => AxiosPromise<Response>;

class Http {
    /**
     * axios 实例
     */
    private axios: AxiosStatic = axios;

    /**
     * 构造函数
     */
    constructor() {
        // 请求超时时间
        this.axios.defaults.timeout = 10000;
        // 基础请求路径
        this.axios.defaults.baseURL = "/";
        // 配置请求拦截器
        this.useRequestInterceptor();
        // 配置响应拦截器
        this.useResponseInterceptor();
    }

    /**
     * 配置请求拦截
     */
    private useRequestInterceptor() {
        this.axios.interceptors.request.use(
            (config: AxiosRequestConfig) => {
                const token = store.getState().user.token;
                if (token) config.headers.token = token;
                return config;
            },
            (error: AxiosError) => {
                return Promise.reject(error);
            }
        );
    }

    /**
     * 配置响应拦截
     */
    private useResponseInterceptor() {
        this.axios.interceptors.response.use(
            (response: AxiosResponse) => {
                if (response.data.code === 401) {
                    store.dispatch({ type: LOGOUT });
                    window.location.href = "/login";
                }
                return Promise.resolve(response);
                // return Promise.resolve(response.data); TODO Axios 无法识别拦截返回了一层 data
            },
            (error: AxiosError) => {
                return Promise.reject(error);
            }
        );
    }

    /**
     * 发送 GET 请求
     * @param url 请求路径
     * @param params 请求参数
     */
    public get: requestFn = (url, params) => {
        return this.axios.get(url, { params });
    };

    /**
     * 发送 POST 请求
     * @param url 请求路径
     * @param data 请求体参数
     */
    public post: requestFn = (url, data) => {
        return this.axios.post(url, data);
    };

    /**
     * 发送 PUT 请求
     * @param url 请求路径
     * @param data 请求体参数
     */
    public put: requestFn = (url, data) => {
        return this.axios.put(url, data);
    };

    /**
     * 发送 DELETE 请求
     * @param url 请求路径
     * @param data 请求体参数
     */
    public delete: requestFn = (url) => {
        return this.axios.delete(url);
    };
}

export default new Http();
